#!/bin/bash


declare -r +i -- funcname="ensure_existing_commands_or_die"
declare -r -i -- exitcodestart=107
declare -r +i -- commandmustnotexist="none"

#---------- nothing to set below

#including funx.bash because caller may pass functions from it which won't be detected as existing otherwise.
_whatarethese_die() { local ec=$1;shift;echo "$@" >&2 ; exit $ec; }
source funx.bash || _whatarethese_die 100 "funx.bash not in PATH"   

declare -r +i -- externalcommandid="file"
all="$@"

if [ -z "$all" ]; then
  echo "This program is used to create calls to function $funcname"
  echo "that are ready to be pasted in a script to ensure that script satisfies the requirements(those commands existing or not)."
  echo "Please pass commands as arguments."
  echo "ie. ls chmod [ if fi \! esac lss 'hex dump' hexdump \"one two three fourthcmd\""
  echo "Commands must not contains spaces in their names, "
  echo " eg. this is two commands: 'hex dump' "
  echo "Note that even though that function can merge all the calls into one by merging the args, I wanted easy command type to have its unique call, for some reason."
  exit 1
fi


#associative array (as opposed to indexed: -a):
declare -A +r +x +g +t -- allencountered=() #all encountered types! containing all the commands for each type
#with -t #trap "echo \"VARIABLE is being used here, current value='${allencountered[@]}'.\"" DEBUG # doesn't show its value, for some unknown reason(to me).

declare -A +r +x +g +t -- trackdups=() #all encountered commands are here, to find dups;

declare +g +x +t +r -- delim="${IFS:0:1}" # IFS is <space><tab><newline>  OR unset or null - either way we don't want to concatenate things(when IFS is null)
declare -r -- delim="${delim:- }" #${parameter:-word} If parameter is unset or null, the expansion of word is substituted. 
#echo ".${delim}."
#delim=1 #just a warning, continues

for each in $all; do
  #cannotDO: warn/err if a command contains spaces (ie. filename with spaces) because those cannot be handled by other functions like: ensure_existing_commands_or_die or $funcname (defined above^) - XXX: can't really do this, because the `for in` above is losing all spaces anyway regardless of initial quotations
  declare +g -- whatis #XXX: split in two as to avoid exit code from `declare` overwriting the one from `type`
  whatis="`type -t -- "$each"`"
  declare +g -- ec=$?
  #  echo "${PIPESTATUS[@]}"
  if test "$ec" -ne "0"; then
    if [ -n "$whatis" ]; then
      echo "Unexpected case when exitcode=$ec and returned '$whatis'."
      exit 128
    else
      #empty return, as expected when non-zero exit code
      #  echo "1${whatis}." >&2
      whatis="$commandmustnotexist" #aka not found
      #  echo "2${whatis}." >&2
    fi
  fi

#  (( trackdups["$each"]++ )) # FIXME: ((: trackdups[[]++ : bad array subscript (error token is "trackdups[[]++ ")  when each="["
#  let trackdups[$each]+=1  # fails too
#  let trackdups[$each]++  # fails too
#  let trackdups['$each']++  # wow this works! but it will likely get fixed, riiight? anyway, reported it.
let 'trackdups[$each]++'  # they say I should quote the arg to let, but I have to single quote it to handle all cases eg. when each="[" (even though double quote works when each="string with spaces")
#  let trackdups['$each']+=1  # wow this works!
#  trackdups[$each]+=1  # works but appends '1' as string!

  if test "${trackdups[$each]}" -gt "1" ; then
    continue; #skip duplicates
  fi


  #add cmd to that specific type
  #declare -- 
  #  echo ".${whatis}." >&2
  if [ ${#allencountered[$whatis]} -gt 0 ]; then
    #prepend a space before appending next item in array, if an item was already in array
    allencountered[$whatis]+="${delim}"
  fi

  allencountered[$whatis]+="$each"
  #echo "$whatis $each" >&2

  #  local alreadyin=0
  #  for eachtypesofar in ${encounteredtypes[@]}; do
  #    if test "$whatis" = "$eachtypesofar" ; then
  #      alreadyin=1
  #      break
  #    fi
  #  done
  #  if test "$alreadyin" -ne "1" ; then
  #    #add it
  #    encounteredtypes+=($whatis)
  #    ${!whatis}
  #    echo "so far: ${encounteredtypes[@]}" >&2
  #  fi
done

#echo "end: '${allencountered[@]}'"
#echo "end: '${#allencountered[@]}'"

echo "# ---- snip ----"
echo "# Generated by: $0 '${all[@]}'" #dups included as given, although they are excluded from the generated code below this line
echo "# ---- "
declare +r -i -- exitcodecnt=$exitcodestart
for eachtype in ${!allencountered[@]}; do
  echo "# -- $eachtype"
  echo -n "$funcname '${allencountered[$eachtype]}' '$eachtype' $exitcodecnt "
  if test "$eachtype" = "$commandmustnotexist" ; then
    echo "\"The specific command that was required to not exist, was in fact found:\""
  elif test "$eachtype" = "$externalcommandid" ; then
    echo "\"The required external command(type:$eachtype) was not found:\""
  else
    echo "\"The required command(type:$eachtype) was not found:\""
  fi
#  ((exitcodecnt++)) #works but, let seems better:
  let 'exitcodecnt++'
done
echo "# ---- snip ----"

declare -i +g -- dupsfound=0
for each in $all; do
  declare +i +g -n -- times='trackdups[$each]' # works with +i, not with -i ... kinda makes sense if it's a ref not an integer
#  declare +i +g +n -- times='trackdups[$each]' # <- why does this work too? even though differently!
#  declare -i +g +n -- times='trackdups[$each]' #<- why does this work? and is equivalent to the below commented one:
#  declare -i +g +n -- times=${trackdups[$each]}
#  echo "times=$times"
  if test "${times}" -gt "1" ; then
    echo "WARNING: '$each' was encountered $times times"
    times=0
#    trackdups[$each]=0
    dupsfound=1
  fi
done
if test "$dupsfound" -gt "0" ; then
  echo "All duplicates were skipped, only first occurrence was considered."
fi

